// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "VideoAgent.h"
#include <unistd.h>
#include <thread>
#include "VideoSocketAgent.h"

VideoAgent* VideoAgent::g_instance = nullptr;
std::mutex VideoAgent::g_instanceLock;
VideoAgent::Garbo VideoAgent::g_garbo;

namespace {
    const char* WORKER_CONF_FILE = "/opt/worker/conf/service.conf";
}

/**
 * @功能描述：Garbo析构函数,删除单例g_instance
 */
VideoAgent::Garbo::~Garbo()
{
    if (VideoAgent::g_instance != nullptr) {
        delete VideoAgent::g_instance;
        VideoAgent::g_instance = nullptr;
    }
}

/**
 * @功能描述：启动socket服务器监听客户端链接
 * @返回值：true,代表初始化成功。false,代表初始化失败
 */
bool VideoAgent::StartServer(int port)
{
    INFO("Begin StartServer");
    if (!VideoSocketAgent::GetInstance()->StartServer(port)) {
        ERR("vmisocketAgent start server failed");
        return false;
    }
    return true;
}

/**
 * @功能描述：初始化消息队列，启动计时器线程和数据发送线程
 * @参数 [in] clientPort：本地监听端口
 * @返回值：true,代表需要重新初始化。false,代表不需要重新初始化
 */
bool VideoAgent::Init(int clientPort, const std::string& encoderType, const std::string& videoFrameType, const std::string& resolution, const std::string& fps)
{
    INFO("Begin Init");
    m_encoderType = encoderType;
    m_videoFrameType = videoFrameType;
    m_resolution = resolution;
    m_frameRate = fps;

    INFO("Worker client init");
    gClientInfo.connectMasterInit(WORKER_CONF_FILE, clientPort, encoderType, videoFrameType, resolution, fps);

    if (!StartServer(clientPort)) {
        ERR("Start server failed");
        return false;
    }
    SetStatus(VIDEO_AGENT_STATUS_IDLE);
    return true;
}

/**
 * @功能描述：销毁消息队列，停止计时器线程和数据发送线程
 */
void VideoAgent::DeInit()
{
    INFO("Begin DeInit");
}

/**
 * @功能描述：初始化并启动计时器线程
 * @返回值：true,代表计时器启动成功。false,代表计时器启动失败
 */
bool VideoAgent::InitTimerThread()
{
    INFO("Security Audit: Begin InitTimerThread");
    m_vmiTimerThread = new (std::nothrow) VmiTimerThread();
    if (m_vmiTimerThread == nullptr) {
        ERR("Security Audit: m_vmiTimerThread new failed");
        return false;
    }

    if (!m_vmiTimerThread->Start()) {
        ERR("Security Audit: m_vmiTimerThread start failed");
        return false;
    }

    return true;
}

/**
 * @功能描述：销毁计时器线程
 */
void VideoAgent::DeinitTimerThread()
{
    INFO("Security Audit: Begin DeinitTimerThread");
    if (m_vmiTimerThread != nullptr) {
        m_vmiTimerThread->Stop();
        delete m_vmiTimerThread;
        m_vmiTimerThread = nullptr;
    }
}

/**
 * @功能描述：VideoAgent的退出函数。设置退出条件，触发AGENT进程退出。
 */
void VideoAgent::Exit()
{
    INFO("Begin Exit");
    SetStatus(VIDEO_AGENT_STATUS_INIT);
}

/**
 * @功能描述：VideoAgent的单例函数
 * @返回值：返回VideoAgent单例对象指针
 */
VideoAgent* VideoAgent::GetInstance()
{
    if (g_instance == nullptr) {
        std::unique_lock<std::mutex> lockGuard(g_instanceLock);
        if (g_instance == nullptr) {
            g_instance = new (std::nothrow) VideoAgent();
            if (g_instance == nullptr) {
                ERR("VideoAgent new failed");
            }
        }
    }
    return g_instance;
}

/**
 * @功能描述：VideoAgent的构造函数
 */
VideoAgent::VideoAgent()
{
}

/**
 * @功能描述：VideoAgent的析构函数
 */
VideoAgent::~VideoAgent()
{
    INFO("VideoAgent desctructor!");
    DeInit();
}


/**
 * @功能描述：设置当前VideoAgent的状态
 * @参数 [in] status：取值有VIDEO_AGENT_STATUS_RUN、VIDEO_AGENT_STATUS_EXIT等5种状态
 */
void VideoAgent::SetStatus(int status)
{
    std::unique_lock<std::mutex> lockGuard(m_statusLock);
    m_status = status;
}

/**
 * @功能描述：处理Agent状态机Idle状态的事件
 * @参数 [in] event：取值有VIDEO_AGENT_EVENT_ACCEPT等6种事件
 * @返回值：true,代表状态迁移成功。false,代表状态迁移失败
 */
bool VideoAgent::HandleStatusIdle(int event)
{
    if (event == VIDEO_AGENT_EVENT_ACCEPT) {
        m_status = VIDEO_AGENT_STATUS_CONNECTED;
        INFO("Agent status from IDLE to CONNECTED");
    } else {
        ERR("Agent event error: %d", event);
        return false;
    }

    return true;
}

/**
 * @功能描述：处理Agent状态机Connected状态的事件
 * @参数 [in] event：取值有VIDEO_AGENT_EVENT_ACCEPT等6种事件
 * @返回值：true,代表状态迁移成功。false,代表状态迁移失败
 */
bool VideoAgent::HandleStatusConnected(int event)
{
    if (event == VIDEO_AGENT_EVENT_BREAK) {
        m_status = VIDEO_AGENT_STATUS_IDLE;
        INFO("Agent status from CONNECTED to IDLE by BREAK");
    } else if (event == VIDEO_AGENT_EVENT_ACCEPT) {
        m_status = VIDEO_AGENT_STATUS_CONNECTED;
        INFO("Agent status from CONNECTED to CONNECTED by ACCEPT");
    } else {
        ERR("Agent event error: %d", event);
        return false;
    }

    return true;
}

/**
 * @功能描述：处理Agent状态机处理函数
 * @参数 [in] event：取值有VIDEO_AGENT_EVENT_ACCEPT等3种事件
 * @返回值：true,代表状态迁移成功。false,代表状态迁移失败
 */
bool VideoAgent::AgentEventNotice(int event)
{
    switch (event) {
        case VIDEO_AGENT_EVENT_ACCEPT:
        case VIDEO_AGENT_EVENT_BREAK:
            break;
        default: {
            ERR("Agent event error: %d", event);
            return false;
        }
    }

    std::unique_lock<std::mutex> lockGuard(m_statusLock);
    switch (m_status) {
        case VIDEO_AGENT_STATUS_IDLE: {
            return HandleStatusIdle(event);
        }
        case VIDEO_AGENT_STATUS_CONNECTED: {
            return HandleStatusConnected(event);
        }
        default: {
            ERR("Agent status error: %d", m_status);
            return false;
        }
    }
}

/**
 * @功能描述：Agent事件循环函数
 */
void VideoAgent::AgentMainLoop()
{
    INFO("Begin AgentMainLoop");
    constexpr int AGENT_LOOP_TIME_OUT = 1000000;
    while (m_status != VIDEO_AGENT_STATUS_INIT) {
        (void)usleep(AGENT_LOOP_TIME_OUT);
        gClientInfo.reportHeartbeat();
    }
    INFO("End AgentMainLoop");
}

/**
 * @功能描述：对云机侧上传的参数进行校验
 */
uint8_t VideoAgent::ParametersValidation(uint8_t encoderType, uint8_t videoFrameType, uint32_t width, uint32_t height, uint32_t fps)
{
    // CPU/GPU校验，worker因c7向前兼容移除编码类型校验

    // H264/H265校验
    if (videoFrameType == FRAME_TYPE_H265 && (m_videoFrameType != "H265" && m_videoFrameType != "MIX")) {
        ERR("H265 encoding is not supported!");
        return VIDEO_FRAME_TYPE_ERR;
    } else if (videoFrameType != FRAME_TYPE_H264 && videoFrameType != FRAME_TYPE_H265) {
        ERR("Invalide video frame type: %d!", videoFrameType);
        return VIDEO_FRAME_TYPE_ERR;
    }
    // 分辨率校验
    uint32_t resolutionMinor = width < height ? width : height;
    if (m_resolution == "720P" && resolutionMinor > 720) {
        ERR("Requested resolution %d is larger than the supported size 720P!", resolutionMinor);
        return RESOLUTION_ERR;
    } else if (m_resolution == "1080P" && resolutionMinor > 1080) {
        ERR("Requested resolution %d is larger than the supported size 1080P!", resolutionMinor);
        return RESOLUTION_ERR;
    } else if (m_resolution != "720P" && m_resolution != "1080P") {
        ERR("Invalid resolution: %d!", m_resolution);
        return RESOLUTION_ERR;
    }

    // 帧率校验
    if (m_frameRate == "20" && fps > 20) {
        ERR("Requested frameRate %d is larger than the supported frameRate 20!", fps);
        return FRAMERATE_ERR;
    } else if (m_frameRate == "30" && fps > 30) {
        ERR("Requested frameRate %d is larger than the supported frameRate 30!", fps);
        return FRAMERATE_ERR;
    } else if (m_frameRate == "60" && fps > 60) {
        ERR("Requested frameRate %d is larger than the supported frameRate 60!", fps);
        return FRAMERATE_ERR;
    } else if (m_frameRate == "0") {
        INFO("No need to check frame rate when worker pattern is direct.");
    }

    return NO_ERR;
}

/**
 * @功能描述：获取当前编码类型
 */
EncodeType VideoAgent::GetEncodeType()
{
    if (m_encoderType == "GPU") {
        return ENCODER_TYPE_REMOTE_HOST_GPU;
    }
    return ENCODER_TYPE_REMOTE_HOST_CPU;
}